-- @Author: Marte
-- @Date:   2020-01-13 08:35:19
-- @Last Modified by:   Marte
-- @Last Modified time: 2020-01-13 08:39:06

--设计方案
--实现 IP 黑名单的功能有很多途径：
--1、在操作系统层面，配置 iptables，拒绝指定 IP 的网络请求；
--2、在 Web Server 层面，通过 Nginx 自身的 deny 选项 或者 lua 插件 配置 IP 黑名单；
--3、在应用层面，在请求服务之前检查一遍客户端 IP 是否在黑名单。

local function close_redis(red)
    if not red then
        return
    end
    --释放连接(连接池实现)
    local pool_max_idle_time = 10000 --毫秒
    local pool_size = 100 --连接池大小
    local ok, err = red:set_keepalive(pool_max_idle_time, pool_size)
    if not ok then
        ngx.say("set keepalive error : ", err)
    end
end

local function errlog(...)
    ngx.log(ngx.ERR, "redis: ", ...)
end

local function duglog(...)
    ngx.log(ngx.DEBUG, "redis: ", ...)
end

local function getIp()
    local myIP = ngx.req.get_headers()["X-Real-IP"]
    if myIP == nil then
        myIP = ngx.req.get_headers()["x_forwarded_for"]
    end
    if myIP == nil then
        myIP = ngx.var.remote_addr
    end
    return myIP;
end

local key = "limit:ip:blacklist"
local ip = getIp();
local shared_ip_blacklist = ngx.shared.shared_ip_blacklist

--获得本地缓存的最新刷新时间
local last_update_time = shared_ip_blacklist:get("last_update_time");

if last_update_time ~= nil then
    local dif_time = ngx.now() - last_update_time
    if dif_time < 60 then --缓存1分钟,没有过期
        if shared_ip_blacklist:get(ip) then
            return ngx.exit(ngx.HTTP_FORBIDDEN) --直接返回403
        end
        return
    end
end

local redis = require "resty.redis"  --引入redis模块
local red = redis:new()  --创建一个对象，注意是用冒号调用的

--设置超时（毫秒）
red:set_timeout(1000)
--建立连接
local ip = "192.168.31.22"
local port = 6379
local ok, err = red:connect(ip, port)
if not ok then
    close_redis(red)
    errlog("limit ip cannot connect redis");
else
    local ip_blacklist, err = red:smembers(key);

    if err then
        errlog("limit ip smembers");
    else
        --刷新本地缓存，重新设置
        shared_ip_blacklist:flush_all();

        --同步redis黑名单 到 本地缓存
        for i,bip in ipairs(ip_blacklist) do
            --本地缓存redis中的黑名单
            shared_ip_blacklist:set(bip,true);
        end
        --设置本地缓存的最新更新时间
        shared_ip_blacklist:set("last_update_time",ngx.now());
    end
end

if shared_ip_blacklist:get(ip) then
    return ngx.exit(ngx.HTTP_FORBIDDEN) --直接返回403
end

--[[
nginx引入

在http部分，配置本地缓存，来缓存redis中的数据，避免每次都请求redis

lua_shared_dict shared_ip_blacklist 1m; #定义ip_blacklist 共享内存变量

location /ipblacklist {
    access_by_lua_file /usr/local/lua/ip_blacklist.lua;
    echo "ipblacklist";
}
--]]